#include "RTSP_Gstreamer.hpp"
#include <unistd.h>
#include <semaphore.h>

queue<Mat>matQ;
cv::Size imgsize;
sem_t g_sem;

CMyStreamer::CMyStreamer()
{
}
CMyStreamer::~CMyStreamer()
{
}

static void need_data (GstElement * appsrc, guint unused, gpointer user_data)
{
    GstBuffer *buffer;
    GstFlowReturn ret;
    GstClockTime timestamp = 0;
    guint gstbuffsize;
    cv::Mat frame;
    sem_post(&g_sem);

    //如果队列中无数据，则挂起
    while(matQ.size() <1)
    {
        usleep(100);
    }
    frame = matQ.front().clone();
    matQ.pop();
    gstbuffsize = imgsize.width * imgsize.height * 3;  
    buffer = gst_buffer_new_allocate (NULL, gstbuffsize, NULL);
    //填充数据
    gst_buffer_fill(buffer,0,frame.data,gstbuffsize);
    //同步
    GST_BUFFER_PTS (buffer) = timestamp;
    GST_BUFFER_DURATION (buffer) = gst_util_uint64_scale_int (1, GST_SECOND, 30);
    timestamp += GST_BUFFER_DURATION (buffer);
    g_signal_emit_by_name (appsrc, "push-buffer", buffer, &ret);
    //释放  解决发送端内存暴涨的问题
    gst_buffer_unref (buffer);
}

static void media_configure (GstRTSPMediaFactory * factory, GstRTSPMedia * media, gpointer user_data)
{

    GstElement *element, *appsrc;
    /* get the element used for providing the streams of the media */
    element = gst_rtsp_media_get_element (media);
    /* get our appsrc, we named it 'mysrc' with the name property */
    appsrc = gst_bin_get_by_name_recurse_up (GST_BIN (element), "mysrc");
    /* this instructs appsrc that we will be dealing with timed buffer */
    gst_util_set_object_arg (G_OBJECT (appsrc), "format", "time");
    /* configure the caps of the video */
    g_object_set (G_OBJECT (appsrc), "caps",
    gst_caps_new_simple ("video/x-raw",
        "format", G_TYPE_STRING, "BGR",
        "width", G_TYPE_INT, imgsize.width, 
        "height", G_TYPE_INT, imgsize.height, 
        "framerate", GST_TYPE_FRACTION, 0, 30, NULL), NULL);
    g_object_set_data_full (G_OBJECT (media), "my-extra-data", user_data,
    (GDestroyNotify) g_free);

    //使用回调函数传递数据
    g_signal_connect (appsrc, "need-data", (GCallback) need_data, NULL);
    gst_object_unref (appsrc);
    gst_object_unref (element);
}

CMyStreamer *rtspstreamer;

void* ThreGst(void* arg)
{
    rtspstreamer = (CMyStreamer*)arg; 
    char str_buf[512];
    imgsize = rtspstreamer->size;
    bool use_h265enc = rtspstreamer->use_h265;

    //gstreamer初始化
    GMainLoop *loop;
    GstRTSPServer *server;
    GstRTSPMountPoints *mounts;
    GstRTSPMediaFactory *factory;
    gst_init (NULL, NULL);
    loop = g_main_loop_new (NULL, FALSE);

    //创建rtsp服务器
    server = gst_rtsp_server_new ();
    g_object_set (server, "service", rtspstreamer->port, NULL);

    //获取服务器的装载点 mounts
    mounts = gst_rtsp_server_get_mount_points (server);
    factory = gst_rtsp_media_factory_new ();

    //添加比特率与编码器的选择
    if(use_h265enc)
    {
        sprintf(str_buf, "( appsrc name=mysrc ! videoconvert ! mpph265enc bps = %d header-mode =1 ! rtph265pay name=pay0 pt=96 )",2 * 1000000); 
        gst_rtsp_media_factory_set_launch (factory,str_buf);
    }
    else
    {
        sprintf(str_buf, "( appsrc name=mysrc ! videoconvert ! mpph264enc bps = %d header-mode =1 ! rtph264pay name=pay0 pt=96 )",2 * 1000000); 
        gst_rtsp_media_factory_set_launch (factory,str_buf);
    }

    //当媒体media_configure准备就绪时通知，每当有人要求时就会调用factory和appsrc创建一个新的管道
    g_signal_connect (factory, "media-configure", (GCallback) media_configure, NULL);

    //将factory附加到 name中(例如/test)
    gst_rtsp_mount_points_add_factory (mounts, rtspstreamer->name.c_str(), factory);

    g_object_unref (mounts);

    //将服务器附加到默认的上下文
    gst_rtsp_server_attach (server, NULL);

    //服务器开始运行
    std::cout << "stream ready at rtsp://127.0.0.1:" << rtspstreamer->port << rtspstreamer->name << std::endl;
    g_main_loop_run (loop);
}

//开线程
bool CMyStreamer::setup(std::string name, cv::Size size, bool use_h265, char *port, int bitrate)
{
    int ret;
    //获取参数
    this->name =name;
    this->size =size;
    this->use_h265 =use_h265;
    this->port =port;
    this->bitrate =bitrate;

    //创建gstreamer线程  线程成功返回0 失败返回错误码
	ret = pthread_create(&thr_gst_ts_thread, NULL,ThreGst, this);
    
    //此处需要从线程中得到管道是否创建成功，成功则设置为TRUE
    if(!ret)
        GstIsOpen = TRUE;
    else
        GstIsOpen = FALSE;
    return GstIsOpen;
}

bool CMyStreamer::isOpened()
{
    //判断线程是否启动

    return GstIsOpen;
}

//给gstreamer管道传递数据  尝试不是用管道，或者添加信号
void CMyStreamer::stream(cv::Mat img_)
{
    sem_init(&g_sem, 0, 0);
    matQ.push(img_);
    sem_wait(&g_sem); 
}

//释放
void CMyStreamer::release()
{
    //线程销毁
    if (rtspstreamer->isOpened())
    {
        pthread_detach(thr_gst_ts_thread);
        GstIsOpen = FALSE;
    }
    cout<<"Gstreamer pipeline is release"<<endl;
}

